<?php

namespace So\Trait;

use Hyperf\DbConnection\Db;
use So\Exception\ControllerException;
use Psr\Http\Message\ResponseInterface;
use Exception;

trait Backend
{
    /**
     * 读取
     * @param $model 模型
     * @return ResponseInterface
     */
    public function read($model): ResponseInterface
    {
        $pageSize = input("pageSize/d") ?: $model->pageSize;
        $sortPk = input("sortPk/s") ?: $model->sortPk;
        $sortBy = input("sortBy/s") ?: $model->sortBy;
        try {
            $model = $this->where($model);
            if (method_exists($this, '_read_model')) {
                $this->_read_model($model);
            }
            $list = $model->orderBy($sortPk, $sortBy)->paginate($pageSize);
            $currentPage = $list->currentPage();
            $total = $list->total();
            $rows = $list->items();
            if (method_exists($this, '_read')) {
                $this->_read($rows, $model);
            }
        } catch (ControllerException $e) {
            Db::rollBack();
            return $this->error($e->getMessage());
        } catch (Exception $e) {
            Db::rollBack();
            return $this->error($this->isDev ? $e->getMessage() : "查询出错");
        }
        return $this->success("", [
            'page' => $currentPage,
            'pageSize' => $pageSize,
            'rows' => $rows,
            'total' => $total,
        ]);
    }

    /**
     * 创建
     * @param $data 验证器
     * @param $model 模型
     * @return ResponseInterface
     */
    public function create($data, $model): ResponseInterface
    {
        Db::beginTransaction();
        try {
            if (method_exists($this, '_create')) {
                $data = $this->_create($data, $model);
            }
            foreach ($data as $k => $v) {
                $model->{$k} = $v;
            }
            $create = $model->save();
            Db::commit();
        } catch (ControllerException $e) {
            Db::rollBack();
            return $this->error($e->getMessage());
        } catch (Exception $e) {
            Db::rollBack();
            return $this->error($this->isDev ? $e->getMessage() : "创建出错");
        }
        if ($create) {
            $data[$model->getPk()] = $model->{$model->getPk()};
            return $this->success('创建成功', $data);
        }
        return $this->error('创建失败');
    }

    /**
     * 更新
     * @param $data 验证器
     * @param $model 模型
     * @return ResponseInterface
     */
    public function update($data, $model): ResponseInterface
    {
        Db::beginTransaction();
        try {
            $pk = $model->getPk();
            if (!isset($data[$pk]) || !$data[$pk]) {
                throw new ControllerException('未传入主键');
            }
            if (method_exists($this, '_update')) {
                $data = $this->_update($data, $model);
            }
            $pk_value = $data[$pk];
            unset($data[$pk]);
            $row = $model::query()->find($pk_value);
            if (!$row) {
                throw new ControllerException('查询不存在');
            }
            foreach ($data as $k => $v) {
                $row->{$k} = $v;
            }
            $update = $row->save();
            Db::commit();
        } catch (ControllerException $e) {
            Db::rollBack();
            return $this->error($e->getMessage());
        } catch (Exception $e) {
            Db::rollBack();
            return $this->error($this->isDev ? $e->getMessage() : "更新出错");
        }
        if ($update) {
            return $this->success('更新成功', $data);
        }
        return $this->error('更新失败');
    }

    /**
     * 删除
     * @param $data 验证器
     * @param $model 模型
     * @return ResponseInterface
     */
    public function delete($data, $model): ResponseInterface
    {
        if (!isset($data['ids']) || !$data['ids']) {
            $ids = input('ids/a');
        } else {
            $ids = $data['ids'];
        }
        if (!$ids) {
            return $this->error('未传入删除项');
        }
        !is_array($ids) && $ids = [$ids];
        Db::beginTransaction();
        try {
            if (method_exists($this, '_delete')) {
                $ids = $this->_delete($ids, $model);
            }
            $model->destroy($ids);
            Db::commit();
        } catch (ControllerException $e) {
            Db::rollBack();
            return $this->error($e->getMessage());
        } catch (Exception $e) {
            Db::rollBack();
            return $this->error($this->isDev ? $e->getMessage() : "删除出错");
        }
        return $this->success('删除成功');
    }

    /**
     * 操作
     * @param $model 模型
     * @return ResponseInterface
     */
    public function operate($model): ResponseInterface
    {
        $ids = input("ids/a");
        if (!$ids) {
            return $this->error('未传入操作项');
        }
        $param = input();
        unset($param['ids']);
        if (!$param) {
            return $this->error('未传入操作字段');
        }
        foreach ($param as $k => $v) {
            if (!in_array($k, $model->operate)) {
                return $this->error('没有权限该选项进行操作');
            }
        }
        Db::beginTransaction();
        try {
            $pk = $model->getPk();
            $list = $model::query()->whereIn($pk, $ids)->get()->toArray();
            $total = 0;
            if (!$list) {
                return $this->error('选择项不存在');
            }
            foreach ($list as $v) {
                $total += $model::query()->where($pk, '=', $v[$pk])->update($param);
            }
            Db::commit();
        } catch (ControllerException $e) {
            Db::rollBack();
            return $this->error($e->getMessage());
        } catch (Exception $e) {
            Db::rollBack();
            return $this->error($this->isDev ? $e->getMessage() : "操作出错");
        }
        if (!$total) {
            return $this->success('操作失败');
        }
        return $this->success('操作成功' . ($total == 1 ? "" : " " . $total . " 条选项"));
    }

    /**
     * 获取where条件
     * @param $model 模型
     * @return array
     */
    public function where($model)
    {
        $timestampsCloumns = $model->timestampsCloumns;
        $withMethods = $model->withMethods ?? [];
        $searchCloumn = $model->searchCloumn;
        $cloumn = $model->getCloumns();
        $model = $model::query();
        $search = input("search/s");
        $filter = json_decode((string)$this->request->input('filter', '{}'), true);
        $operator = ['=', '!=', '>', '>=', '<', '<=', 'like', 'not like', 'between', 'not between'];
        $operatorAction = ['between' => "whereBetween", 'not between' => "whereNotBetween"];
        $withFilter = [];
        $with = [];
        $where = function ($k, $content, $model) use ($operator, $operatorAction, $timestampsCloumns) {
            if (count($content) == 2 && in_array($content[1], $operator)) {
                in_array($content[1], ['between', 'not between']) && $content[0] = explode(',', $content[0]);
                is_array($content[0]) && $model->{$operatorAction[$content[1]]}($k, array_map(function ($c) use ($k, $timestampsCloumns) {
                    return in_array($k, $timestampsCloumns) ? strtotime($c) : $c;
                }, $content[0]));
                !is_array($content[0]) && $model->where($k, $content[1], (string)$content[0]);
            }
            return $model;
        };
        foreach ($filter as $k => $v) {
            $content = array_filter(explode($this->searchBreak, $v));
            if (strpos($k, '.') !== false) {
                $k = explode('.', $k);
                !isset($withFilter[$k[0]]) && $withFilter[$k[0]] = [];
                $withFilter[$k[0]][] = [$k[1], $content];
                continue;
            }
            in_array($k, $cloumn) && $model = $where($k, $content, $model);
        }
        foreach ($withFilter as $k => $v) {
            in_array($k, $withMethods) && $model->whereHas($k, function ($query) use ($where, $v) {
                foreach ($v as $c) $query = $where($c[0], $c[1], $query);
            });
        }
        $withMethods && $model->with($withMethods);
        if ($search && $searchCloumn) {
            $orWhere = [];
            foreach ($searchCloumn as $k => $v) {
                in_array($v, $cloumn) && $orWhere[] = [$v, 'like', "%{$search}%", 'OR'];
            }
            $model->where($orWhere);
        }
        return $model;
    }

    /**
     * 拖拽排序
     * @param $model 模型
     * @param string $parent_pk 上级ID
     * @return ResponseInterface
     */
    public function draggable($model, $parent_pk = 'pid'): ResponseInterface
    {
        $id = input('id/d');
        $adjoin = input('adjoin/d');
        $type = input('type/s');
        if (!$id || !$adjoin) {
            return $this->error("缺少数据");
        }
        if (!in_array($type, ['before', 'after', 'inner'])) {
            return $this->error("类型错误");
        }
        $row = $model::query()->find($id);
        $adjoin = $model::query()->find($adjoin);
        if (!$row || !$adjoin) {
            return $this->error("数据不存在");
        }
        $data = [];
        if ($type == 'before' || $type == 'after') {
            if ($row['pid'] !== $adjoin['pid']) {
                $data['pid'] = $adjoin['pid'];
            }
            $data['sort'] = ($type == 'before' ? $adjoin['sort'] + 1 : $adjoin['sort'] - 1);
        } else {
            $data['pid'] = $adjoin['id'];
        }
        $model::query()->where("id", '=', $row['id'])->update($data);
        return $this->success('操作成功');
    }
}